package com.petshop.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.util.RandomUtil;
import com.alibaba.druid.util.StringUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.petshop.dto.LoginDTO;
import com.petshop.dto.PasswordClass;
import com.petshop.dto.UserDTO;
import com.petshop.mapper.UsersMapper;
import com.petshop.pojo.Users;
import com.petshop.service.UsersService;
import com.petshop.utils.*;
import jakarta.servlet.http.HttpSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.BitFieldSubCommands;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import static com.petshop.utils.RedisConstant.*;

/**
 * @author 20289
 * @description 针对表【users(存储用户基本信息)】的数据库操作Service实现
 * @createDate 2025-02-27 11:17:00
 */
@Service
public class UsersServiceImpl extends ServiceImpl<UsersMapper, Users>
        implements UsersService {
    @Autowired
    private UsersMapper usersMapper;
    @Autowired
    private JwtHelper jwtHelper;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 登录业务
     * 首先根据账号查询用户对象， loginUsers
     * 如果账号是空的，没有此账号的用户，说明查询失败，返回501错误
     * 如果账号不空，则继续对比密码
     * 密码正确，生成token
     * 密码错误，返回503错误
     *
     * @param users
     * @return
     */
    @Override
    public Result login(Users users) {
        LambdaQueryWrapper<Users> lambdaQueryWrapper =
                new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(Users::getUsername, users.getUsername());
        Users loginUsers = usersMapper.selectOne(lambdaQueryWrapper);
        //判断账号是否正确
        if (loginUsers == null) {
            return Result.build(null, ResultCodeEnum.USERNAME_ERROR);
        }
        //账号正确，判断密码是否正确
        if (!StringUtils.isEmpty(users.getPassword()) &&
                MD5Util.encrypt(users.getPassword()).equals(loginUsers.getPassword())) {
            //密码正确，登录成功。根据用户id生成token，将token封装到result并返回
            String token = jwtHelper.createToken(Long.valueOf(loginUsers.getUserId()));
            //7.保存用户信息到redis
            //7.1.生成一个token 作为登录令牌
            //7.2.将user对象转换成hash结构
            UserDTO userDTO = BeanUtil.copyProperties(loginUsers, UserDTO.class);
            Map<String, Object> userMap = BeanUtil.beanToMap(
                    userDTO,
                    new HashMap<>(),
                    CopyOptions
                            .create()
                            .setIgnoreNullValue(true)
                            .setFieldValueEditor((fieldName,fieldValue)->fieldValue.toString()));
            //7.3.保存在redis
            String tokenKey = LOGIN_USER_KEY + token;
            stringRedisTemplate.opsForHash().putAll(tokenKey, userMap);
            //7.4.设置有效期
            stringRedisTemplate.expire(LOGIN_USER_KEY, LOGIN_USER_TTL, TimeUnit.MINUTES);
            return Result.ok(token);
        }
        return Result.build(null, ResultCodeEnum.PASSWORD_ERROR);
    }

    /**
     * @param loginDTO
     * @param session
     * @return
     */
    @Override
    public Result login1(LoginDTO loginDTO, HttpSession session) {
        //1.手机号验证
        String phone = loginDTO.getPhone();
        if (!Check.isPhone(phone)) {
            return Result.error("手机号格式错误");
        }
        //2.从redis获取验证码，校验验证码
        String cacheCode = stringRedisTemplate.opsForValue().get(LOGIN_CODE_KEY + phone);
        String code = loginDTO.getCode();
        if (cacheCode == null || !cacheCode.equals(code)) {
            //3.不一致，报错
            return Result.error("验证码错误！");
        }
        //4.一致，根据手机号查询用户
        Users user = query().eq("phone", phone).one();
        //5.判断用户是否存在
        if (user == null) {
            //6.不存在，创建新用户
            user = createUserByPhone(phone);
        }
        //7.保存用户信息到redis
        //7.1.生成一个token 作为登录令牌
        String token = jwtHelper.createToken(Long.valueOf(user.getUserId()));
        //7.2.将user对象转换成hash结构
        UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);
        Map<String, Object> userMap = BeanUtil.beanToMap(
                userDTO,
                new HashMap<>(),
                CopyOptions
                        .create()
                        .setIgnoreNullValue(true)
                        .setFieldValueEditor((fieldName,fieldValue)->fieldValue.toString()));
        //7.3.保存在redis
        String tokenKey = LOGIN_USER_KEY + token;
        stringRedisTemplate.opsForHash().putAll(tokenKey, userMap);
        //7.4.设置有效期
        stringRedisTemplate.expire(LOGIN_USER_KEY, LOGIN_USER_TTL, TimeUnit.MINUTES);
        //8.返回token
        return Result.ok(token);
    }

    private Users createUserByPhone(String phone) {
        Users user = new Users();
        user.setPhone(phone);
        user.setPassword("");
        user.setUsername(RandomUtil.randomString(6));
        user.setNickname(RandomUtil.randomString(6));
        save(user);
        return user;
    }

    /**
     * 生成手机验证码
     *
     * @param phone
     * @param session
     * @return
     */
    @Override
    public Result sendCode(String phone, HttpSession session) {
        //1.校验手机号
        if (!Check.isPhone(phone)) {
            //2.如果不符合，返回错误信息
            return Result.error("手机号格式错误");
        }
        //3.如果符合，生成验证码
        String code = RandomUtil.randomNumbers(6);
        //4.保存验证码到redis，手机号作为key
        stringRedisTemplate.opsForValue().set(LOGIN_CODE_KEY + phone, code, LOGIN_CODE_TTL, TimeUnit.MINUTES);
        //5.发送验证码
//-----------暂时跳过-----------------
        //6.返回ok
        return Result.ok(code);
    }

    /**
     * 实现签到
     * key:userId+日期
     * value: 0/1
     * @return
     */
    @Override
    public Result sign() {
        //1.获取当前用户
        Integer userId = UserHolder.getUser().getUserId();
        //2.获取当前日期
        LocalDateTime now = LocalDateTime.now();
        //3.拼接key
        String format = now.format(DateTimeFormatter.ofPattern(":yyyyMM"));
        String key = USER_SIGN_KEY+userId+format;
        //4.获取今天是本月的第几天
        int dayOfMonth = now.getDayOfMonth(); //1到31
        //5.签到写入redis
        //在BitMap是从下标0开始统计，因此要dayOfMonth-1；true代表值为1，false代表为0
        Boolean isSign = stringRedisTemplate.opsForValue().setBit(key, dayOfMonth - 1, true);

        return Result.ok("签到成功！");
    }

    /**
     * 统计截止到当前日期的连续签到天数
     *
     * @return
     */
    @Override
    public Result signCount() {
        //1.获取当前用户
        Integer userId = UserHolder.getUser().getUserId();
        //2.获取当前日期
        LocalDateTime now = LocalDateTime.now();
        //3.拼接key
        String format = now.format(DateTimeFormatter.ofPattern(":yyyyMM"));
        String key = USER_SIGN_KEY+userId+format;
        //4.获取今天是本月的第几天
        int dayOfMonth = now.getDayOfMonth(); //1到31
        //5，获取本月截止到今天为止的所有签到记录，返回一个十进制数字
        //BITFIELD key get u[dayOfMonth] 0
        /*
        BitFieldSubCommands 是子命令，用来完成get u[dayOfMonth] 0这些参数的传入
        返回值是集合，几个命令就有几个集合，我只用了一个get命令，因此集合只有一个元素
         */
        List<Long> result = stringRedisTemplate.opsForValue().bitField(key,
                BitFieldSubCommands.create()
                        .get(BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth))
                        .valueAt(0));
        //判断签到情况
        if (result == null||result.isEmpty()) {
            return Result.error("没有签到记录！");
        }
        Long num = result.get(0);
        if (num == null||num == 0) {
            return Result.error("没有签到记录");
        }
        //6.十进制转换成二进制循环遍历
        int count = 0;
        while(true) {
            //6.1让数字与1做与运算，得到数字的最后一个bit位
           if((num&1)==0){
               //如果为0 说明没签到
               break;
           }else {
               //如果不为0，抛弃最后一个bit，继续下一个bit
               count++;
           }
            //右移1位
            num = num>>>1;
        }
        return Result.ok(count);
    }


    @Override
    public Users getUserInfo(String token) {
        int userId = jwtHelper.getUserId(token).intValue();
        LambdaQueryWrapper<Users> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(Users::getUserId, userId);
        Users user = usersMapper.selectOne(lambdaQueryWrapper);
        //把密码置空 保证安全
//        user.setPassword("");
        return user;
    }

    @Override
    public Result checkUsername(String username) {
        LambdaQueryWrapper<Users> lambdaQueryWrapper =
                new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(Users::getUsername, username);

        Long count = usersMapper.selectCount(lambdaQueryWrapper);
        if (count == 0) {
            return Result.ok(null);
        }

        return Result.build(null, ResultCodeEnum.USERNAME_USED);
    }

    /**
     * 注册业务
     *
     * @param user
     * @return
     */
    public Result regist(Users user) {
        //检查账号是否被注册
        LambdaQueryWrapper<Users> lambdaQueryWrapper =
                new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(Users::getUsername, user.getUsername());
        Long count = usersMapper.selectCount(lambdaQueryWrapper);
        if (count > 0) {
            return Result.build(null, ResultCodeEnum.USERNAME_USED);
        }
        //密码加密
        user.setPassword(MD5Util.encrypt(user.getPassword()));
        usersMapper.insert(user);
        return Result.ok(null);
    }

    /**
     * 修改用户信息
     *
     * @param user
     * @return
     */
    @Override
    public Result updateUserInfo(Users user) {
        user.setUpdatedAt(new Date());
        UpdateWrapper<Users> updateWrapper = new UpdateWrapper<>();
        updateWrapper.eq("user_id", user.getUserId());
        int update = usersMapper.update(user, updateWrapper);
        if (update != 1) {
            return Result.error("修改失败！请重试");
        }
        return Result.ok("修改成功！");
    }

    /**
     * @param amount
     * @param token
     * @return
     */
    @Override
    public Result recharge(BigDecimal amount, String token) {
        Integer userId = jwtHelper.getUserId(token).intValue();
        LambdaQueryWrapper<Users> lambdaQueryWrapper
                = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(Users::getUserId, userId);
        Users user = usersMapper.selectOne(lambdaQueryWrapper);
        int i = usersMapper.recharge(amount, userId);
        if (i > 0) {
            return Result.ok("充值成功！");
        }
        return Result.error("充值失败！请重试");
    }

    /**
     * 改密码
     *
     * @param passwordClass
     * @param token
     * @return
     */
    @Override
    public Result updatePassword(PasswordClass passwordClass, String token) {
        //将传入的新/原密码加密
        String oldEncrypt = MD5Util.encrypt(passwordClass.getOldPassword());
        String newEncrypt = MD5Util.encrypt(passwordClass.getNewPassword());
        //获取当前操作用户的密码，与传入的对比
        int userId = jwtHelper.getUserId(token).intValue();
        LambdaQueryWrapper<Users> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(Users::getUserId, userId);
        Users user = usersMapper.selectOne(lambdaQueryWrapper);
        //对比新输入的密码是不是和旧密码相同
        boolean equals1 = user.getPassword().equals(newEncrypt);
        if (equals1) {
            return Result.error("新旧密码相同！请重新设置");
        }
        boolean equals = user.getPassword().equals(oldEncrypt);
        if (equals) {
            //改密码

            usersMapper.updatePassword(newEncrypt, userId);
            return Result.ok("密码修改成功！");
        }
        return Result.error("旧密码输入错误！");
    }


}




